// client.cpp
#define _WIN32_DCOM
#include <olectl.h>
#include <conio.h>
#include <iostream.h>
#include "Component\component.h"

// This sink can implement any interface at runtime
// In C++, struct == public class
struct CStink // class for the generic sink object
{
	// The First item must be a pointer to the vtbl of the sink object
	// Here we can see the binary standard that defines COM
	void (__stdcall **pVtbl)(); // Declared as a pointer to one or more function pointers

	// Next comes the object's data
	long m_cRef; // reference counting variable
};

// Non-static methods in a class always have an implicit 'this' argument that points to the current object
// Since these methods are not part of a class we make beleive...
// Every method in the interface must pretend to have a 'this' argument as all callers will provide it
ULONG __stdcall CStink_AddRef(CStink* me)
{
	// me is a stand-in for 'this', a reserved keyword in C++
	// Everything must be explicitly accessed through the pointer to the object
	// Now we can appreciate what C++ does automatically
	return ++me->m_cRef;
}

ULONG __stdcall CStink_Release(CStink* me)
{
	if(--me->m_cRef != 0)
		return me->m_cRef;
	delete me; // We can even use delete
	return 0;
}

HRESULT __stdcall CStink_QueryInterface(CStink* me, REFIID riid, void** ppv)
{
	if(riid == IID_IUnknown || riid == IID_IOutGoing)
		*ppv = &me->pVtbl; // Here's a pointer to our interface
	else 
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}
	CStink_AddRef(me);
	return S_OK;
}

// This method could be generated dynamically based on type information
HRESULT __stdcall GotMessage(CStink *me, int Message)
{
	MessageBeep(MB_OK);
	cout << "StinkGotMessage() is " << (char)Message << endl;
	return S_OK;
}

void main()
{
	cout << "Client: Calling CoInitialize()" << endl;
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	
	IUnknown* pUnknown;
	cout << "Client: Calling CoCreateInstance() " << endl;
	CoCreateInstance(CLSID_InsideCOM, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);

	IConnectionPointContainer* pConnectionPointContainer;
	HRESULT hr = pUnknown->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
	if(FAILED(hr))
		cout << "Not a connectable object, going to crash now" << endl;

	IConnectionPoint* pConnectionPoint;
	hr = pConnectionPointContainer->FindConnectionPoint(IID_IOutGoing, &pConnectionPoint);
	cout << "FindConnectionPoint returns " << hr << " pConnectionPoint = " << pConnectionPoint << endl;

	// Instantiate the object, sort of...
	CStink* mySink = new CStink;
	int num_methods = 4; // 3 for IUnknown + number of methods in outgoing interfaces

	// Allocate memory for the vtbl and cast the returned void* to a pointer to a bunch of funcion pointers
	void (__stdcall **IStink)() = (void (__stdcall **)())CoTaskMemAlloc(num_methods * sizeof(void*)); // 4 bytes per pointer

	// Initialize the vtbl to point to our implementations
	// These three always come first! (in this order)
	*(IStink + 0) = (void (__stdcall *)())CStink_QueryInterface;
	*(IStink + 1) = (void (__stdcall *)())CStink_AddRef;
	*(IStink + 2) = (void (__stdcall *)())CStink_Release;

	// Now add any additional methods to the vtbl based on the available type 
	// information for the outgoing interface
	*(IStink + 3) = (void (__stdcall *)())GotMessage; // IOutGoing only has one additional method

	// Give the sink a brain
	mySink->pVtbl = IStink;

	// Initialize the sink's reference counter
	mySink->m_cRef = 0;

	DWORD dwCookie;
	hr = pConnectionPoint->Advise((IUnknown*)mySink, &dwCookie);
	cout << "Client: IConnectionPoint::Advise returned " << hr << endl;

	cout << "Press any key to exit" << endl;
	_getch();

	pConnectionPoint->Unadvise(dwCookie);

	// Now free the vtbl, the has probably already deleted itself
	CoTaskMemFree(IStink);

	pConnectionPoint->Release();
	pConnectionPointContainer->Release();

	cout << "Client: Calling Release() for pUnknown" << endl;
	hr = pUnknown->Release();

	cout << "Client: Calling CoUninitialize()" << endl;
	CoUninitialize();
}